package ws.performancetest.tests.synchronization.db;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import ws.utils.jdbc.DBConnection;

/**
 * This class handles the test result, ie, time measurements.
 *
 * <p>
 * Its job is to store each test result on a list, and then store then on
 * the database. The database storage is done on user demand, using 
 * {@link #writeTestResultsToBD() }.
 * </p>
 *
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
public class DBTestResultManager implements IDBRegister, IDBReporter, IDBWriter {

  /** Object reference. */
  private static DBTestResultManager reference = null;
  /** List used to store the current test results. */
  private final BlockingQueue<DBTestResult> list = new LinkedBlockingQueue<DBTestResult>();
  /** Database connection. */
  private static Connection dbcon;
  /* id of the pair (TestGroup, Host) that owns the current result list. */
  private static int testGroup_Host_id;

  /** Constructor: prevent multiple instantiation. */
  private DBTestResultManager() throws SQLException {
    dbcon = DBConnection.getDBCon();
  }

  /**
   * Get object reference.
   * 
   * @return reference to {@link DBTestResultManager}.
   * 
   * @throws SQLException if there is a error establishing connection.
   */
  public static DBTestResultManager getReference() throws SQLException {
    if (reference == null) {
      reference = new DBTestResultManager();
    }
    return reference;
  }

  /**
   * Add test result to the list.
   *
   * <p>Attention: this will not store the result on the database, to store
   * all current test results on the database use {@link #writeTestResultsToBD() }.
   * </p>
   *
   * @param info test result information.
   */
  public void addTestResult(DBTestResult info) {
    list.add(info);
    //System.out.println(DBTestResultManager.class.getName() + ".addTestResult:\n\t" + info);
  }

  /**
   * Write the current test results list to the database.
   *
   * @throws SQLException if some test fails to be inserted on the database,
   * none will be stored.
   */
  public void writeTestResultsToBD() throws SQLException {
    PreparedStatement stmt = null;
    String sql = null;
    try {
      dbcon.setAutoCommit(false);

      sql = "INSERT INTO Results (TestId, AccessTime, TestGroup_Host_id) VALUES (?, ?, ?)";
      stmt = dbcon.prepareStatement(sql);

      for (DBTestResult info : list) {
        stmt.setInt(1, info.getTestId());
        stmt.setLong(2, info.getTime());
        stmt.setInt(3, testGroup_Host_id);

        stmt.executeUpdate();
      }
      
      dbcon.commit();
    } catch (SQLException ex) {
      dbcon.rollback();
      throw ex;
    } finally {
      dbcon.setAutoCommit(false);
    }
  }

  /**
   * Create an {@link IDBRegister} instance. It also erases all unsaved test
   * results.
   *
   * @return {@code reference} cast.
   *
   * @throws SQLException
   */
  public static IDBRegister createIDBRegister() throws SQLException {
    /* start a new recording process. */
    reference.list.clear();
    return (IDBRegister) getReference();
  }

  /**
   * Create an {@link IDBReporter} instance.
   *
   * @return {@code reference} cast.
   *
   * @throws SQLException
   */
  public static IDBReporter createDBReporter() throws SQLException {
    return (IDBReporter) getReference();
  }

  /**
   * Create an {@link IDBRegister} instance. It also erases all unsaved test
   * results.
   *
   * @return {@code reference} cast.
   *
   * @throws SQLException
   */
  public static IDBWriter createDBWriter() throws SQLException {
    return (IDBWriter) getReference();
  }

  /**
   * Change id of the pair (TestGroup, Host) that owns the current result list.
   *
   * @param testGroup_Host new id of the pair (TestGroup, Host) that owns the current result list.
   */
  public void setResultsOwner(int testGroup_Host) {
    testGroup_Host_id = testGroup_Host;
  }

  public BlockingQueue<DBTestResult> readFromDB() {
    throw new UnsupportedOperationException("Not supported yet.");
  }

  public void writeToFile() {
    throw new UnsupportedOperationException("Not supported yet.");
  }
}
